2

For a typical C++ header only library located on, e.g., github, at: https://github.com/username/library_name,

has a directory structure with an include/library_name folder like:

  • include/library_name

containing all the library sources. This is typically installed by users to, e.g., under Linux: /usr/local/include/library_name.

I need a cmake script for using the library in external projects portably (across Linux, MacOs, BSD, Windows).

It should:

  • find if the library is installed, if the version is over a threshold, use the installed library

  • otherwise, get the library from github, configure it as an external project, and put it in the system include path so that it can be used by the project as if it were installed.

What is the correct way of achieving this with CMake?

gnzlbg
  • 7,135
  • 5
  • 53
  • 106
  • Why not just include the headers in your project and not rely on external sources? – trenki Feb 20 '15 at 14:10
  • Such that you are always using the latest version of the library, that way you don't have to update it within your own project when bugs get fixed, and in case of small API breakages you can reparate them/complain when they happen. Dealing with API breakages 6 months/1 year later is really hard. – gnzlbg Feb 20 '15 at 14:24
  • Adding the headers to the system include dirs seems like a bad idea to me. Why not `/usr/local/include` or somewhere under `$HOME` ? – Jonathan Wakely Feb 20 '15 at 14:31
  • @JonathanWakely yes, you are right. I meant to say the "cmake include path". My answer below does just that. It just sets `LIBRARY_NAME_INCLUDE_DIRS` and the user can decide if it wants to do `include_directory(${LIBRARY_NAME_INCLUDE_DIRS})` or something else. – gnzlbg Feb 20 '15 at 14:54

1 Answers1

1

I finally got it to work, this FindLibraryName.cmake tries to find the library and if it doesn't find it, gets it from github and sets it up as an external project:

# Find the Library_Name include directory
# The following variables are set if Library_Name is found.
#  Library_Name_FOUND        - True when the Library_Name include directory is found.
#  Library_Name_INCLUDE_DIRS - The path to where the poco include files are.
# If Library_Name is not found, Library_Name_FOUND is set to false.

find_package(PkgConfig)

# Allow the user can specify the include directory manually:
if(NOT EXISTS "${LIBRARY_NAME_INCLUDE_DIR}")
     find_path(LIBRARY_NAME_INCLUDE_DIR
         NAMES library_name/library_name.hpp 
         DOC "Library_Name library header files"
     )
endif()

if(EXISTS "${LIBRARY_NAME_INCLUDE_DIR}")
  include(FindPackageHandleStandardArgs)
  mark_as_advanced(LIBRARY_NAME_INCLUDE_DIR)
else()
  include(ExternalProject)
  ExternalProject_Add(library_name
    GIT_REPOSITORY https://github.com/username/library_name.git
    TIMEOUT 5
    CMAKE_ARGS -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}
    PREFIX "${CMAKE_CURRENT_BINARY_DIR}"
    INSTALL_COMMAND "" # Disable install step, is a header only lib!
    )

  # Specify include dir
  ExternalProject_Get_Property(library_name source_dir)
  set(LIBRARY_NAME_INCLUDE_DIRS ${source_dir}/include)
endif()

if(EXISTS "${LIBRARY_NAME_INCLUDE_DIR}")
  set(Library_Name_FOUND 1)
else()
  set(Library_Name_FOUND 0)
endif()

Then in the projects CMakeLists.txt, just add:

find_package(Library_Name)
include_directories(${LIBRARY_NAME_INCLUDE_DIRS})

You can replace GIT with SVN above and provide the URL of an svn repo and it will work as well. Other version control systems are also available.

gnzlbg
  • 7,135
  • 5
  • 53
  • 106