Liwei
Liwei

Reputation: 481

Integrate CMake subproject with another

I've wrote a C++ library MyLib and I'd like to integrate it with another project ExternPro. So in ExternPro I wrote CMakeLists.txt like this:

add_subdirectory(MyLib)
ADD_EXECUTABLE(test test.cpp)
include_directories(${MyLib_INCLUDE_DIRS})
target_link_libraries(test ${MyLib_LIBRARIES})

To set variables MyLib_LIBRARIES and MyLib_INCLUDE_DIRS I wrote:

set(MyLib_LIBRARIES ${PROJECT_SOURCE_DIR}/src/MyLib.a CACHE INTERNAL "")
set(MyLib_INCLUDE_DIRS ${PROJECT_SOURCE_DIR}/include CACHE INTERNAL "")

But something wrong "No rule to make target MyLib/src/MyLib.a, needed by test. Stop."

So my question is, how should I wrote CMakeLists.txt correctly so cmake could help me build MyLib first and then take care of dependencies of ExternPro?

Upvotes: 2

Views: 1641

Answers (3)

EmDroid
EmDroid

Reputation: 6066

If those are two separate projects, I normally use "CMake find scripts" to reference one library from the other: http://www.vtk.org/Wiki/CMake:How_To_Find_Libraries#Writing_find_modules

But I normally use slightly different find script than described there (FindMyLibrary.cmake):

# Find MyLibrary installation
#
# This module needs following variables specified (e.g. through cmake -Dvar=)
#  MyLibrary_ROOT_DIR         - root directory of the library installation
#
# This module defines the following variables:
#  MyLibrary_INCLUDE_DIRS     - Where to find the public headers
#  MyLibrary_LIBRARIES        - List of mandatory and optional libraries
#  MyLibrary_FOUND            - True if an installation was found
#
# Configuration variables for tis module:
#  MyLibrary_USE_STATIC_LIBS  - Set to ON to force the use of the static
#                               libraries. Default is OFF.

# If MyLibrary_ROOT_DIR was defined in the environment, use it.
if(NOT MyLibrary_ROOT_DIR AND NOT $ENV{MyLibrary_ROOT_DIR} STREQUAL "")
  set(MyLibrary_ROOT_DIR $ENV{MyLibrary_ROOT_DIR})
endif()

if(NOT MyLibrary_ROOT_DIR)
    set(MyLibrary_ROOT_DIR /usr)
endif()

message(STATUS "Using MyLibrary_ROOT_DIR: ${MyLibrary_ROOT_DIR}")

find_path(MyLibrary_INCLUDE_DIRS
          NAMES mylib/mylib.hpp
          PATHS ${MyLibrary_ROOT_DIR}
          PATH_SUFFIXES include)

# Here we set the default components
if(NOT MyLibrary_FIND_COMPONENTS)
  set(MyLibrary_FIND_COMPONENTS mylibrary)
endif()

# Support preference of static libs by adjusting CMAKE_FIND_LIBRARY_SUFFIXES
if(MyLibrary_USE_STATIC_LIBS)
  set(_mylib_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES})
  if(WIN32)
    set(CMAKE_FIND_LIBRARY_SUFFIXES .lib .a ${CMAKE_FIND_LIBRARY_SUFFIXES})
  else()
    set(CMAKE_FIND_LIBRARY_SUFFIXES .a)
  endif()
endif()

foreach(COMPONENT ${MyLibrary_FIND_COMPONENTS})
    find_library(MyLibrary_${COMPONENT}_LIBRARY
                 NAMES ${COMPONENT}
                 HINTS ${MyLibrary_ROOT_DIR}
                 PATH_SUFFIXES lib64 lib
                 NO_DEFAULT_PATH)
    set(MyLibrary_LIBRARIES ${MyLibrary_LIBRARIES} ${MyLibrary_${COMPONENT}_LIBRARY})
endforeach()

# Restore the original find library ordering
if(MyLibrary_USE_STATIC_LIBS)
  set(CMAKE_FIND_LIBRARY_SUFFIXES ${_mylib_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES})
endif()

# handle the QUIETLY and REQUIRED arguments and set MyLibrary_FOUND to
# TRUE if all listed variables are TRUE
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(
        MyLibrary "Could NOT find MyLibrary: set MyLibrary_ROOT_DIR to a proper location"
        MyLibrary_LIBRARIES
        MyLibrary_INCLUDE_DIRS)

mark_as_advanced(MyLibrary_INCLUDE_DIRS MyLibrary_LIBRARIES)

Then used like this:

find_package(MyLibrary REQUIRED)
include_directories(SYSTEM ${MyLibrary_INCLUDE_DIRS})

target_link_libraries(${TARGET}
    ${MyLibrary_LIBRARIES}
)

Basically, it goes like this:

  1. The library is built and installed (make install) to either the default location (e.g. /usr), or some other location (usual in development).
  2. The FindMyLibrary.cmake is part of the library installation (for RPM the library devel package) and installs as well (into ${instdir}/share/cmake/Modules, for example).
  3. The dependent project then adds the path to the CMAKE_MODULE_PATH and uses the find script to find the public headers and libraries as installed.

The advantage is that this way you can either use it during the development (when you have the library sources and build the library), or without the library sources as well (with just the library and headers - the devel package - installed in the system).

Similar technique is normally used by Boost etc. (the find scripts provided already by the CMake).

Upvotes: 1

David Marquant
David Marquant

Reputation: 2237

First of all this doesn't work because variables set in a subdirectory are not set for the parent directory.

So to solve this properly you should define MyLib like:

add_library(MyLib ...)
target_include_directories(MyLib INTERFACE ${PROJECT_SOURCE_DIR}/include)

And for the ExternPro you just need to link to MyLib:

target_link_libraries(test MyLib)

This will automatically add the include directory to test and link MyLib properly.

Upvotes: 1

Tsyvarev
Tsyvarev

Reputation: 65908

Instead of path to the library, use library target for target_link_libraries.

Assuming your library project contains

add_library(MyLib ...)

Linking of executable in main project should be performed with

target_link_libraries(test MyLib)

Upvotes: 1

Related Questions