3

I have a code folder structure like this:

project/
  CMakeLists.txt
  src/
    project.cpp
  lib/
    libA/
      CMakeLists.txt
      src/
        libA.cpp
      include/
        libA.h
    libB/
      CMakeLists.txt
      src/
        libB.cpp
      include/
        libB.h

Now, libA is totally standalone, so I have a CMakeLists.txt similar to this:

cmake_minimum_required(VERSION 2.8)
project ( libA )
set ( LIBA_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/include PARENT_SCOPE )
include_directories ( include/ )

file (GLOB LIBA_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}
  src/libA.cpp
  )
add_library         ( libA STATIC ${LIBA_SOURCES} )

However, libB depends on libA. How can I export the libA.a output path to be used for linking to libB, and the corresponding include path?

And furthermore, how do I pass that data to the parent CMakeLists.txt for the main project?

manatttta
  • 3,054
  • 4
  • 34
  • 72
  • For a discussion of the possibilities see e.g. [Making cmake library accessible by other cmake packages automatically](http://stackoverflow.com/questions/33462209/making-cmake-library-accessible-by-other-cmake-packages-automatically). One other possibility would be to propagate the path of the first lib to the other lib through CMake command line parameters like done [here](http://stackoverflow.com/questions/36173840/how-to-instruct-cmake-to-use-the-build-architecture-compiler). – Florian Apr 26 '16 at 19:02
  • @Florian correct me if I'm wrong, but your answer looks like it is intended for cases where there are two _seperate_ projects. Does `find_package` work if the library is included as a subdirectory and hasn't been built yet? – Nicolas Holthaus Apr 26 '16 at 19:18
  • You're right. I thought you were asking how to integrate a standalone CMake enabled library. So in your case the answer depends on your project's setup/environment. Is `libA` also used in other projects? Do `libB` and `libA` have the same release cycles? Do you deliver the source plus CMake files and the targeted build environment is unknown? See e.g. my answer [here](http://stackoverflow.com/questions/33443164/cmake-share-library-with-multiple-executables) or my question [here](http://stackoverflow.com/questions/31512485/cmake-how-to-setup-source-library-and-cmakelists-txt-dependencies). – Florian Apr 27 '16 at 12:28

3 Answers3

2

I haven't done this personally, so this answer may only be a starting point, but Qt uses cmake and is able to tie all the include directories and library dependencies into each 'module' (library). If you accomplish that, then you can simply do

add_executable(myExe ...)
target_link_libraries(myExe libB)

to the executable in your main cmake project.

Looking through the Qt scripts, it appears what you need to set (for each library) is:

add_library(libA ...)
set_target_properties(libA PROPERTIES
    "INTERFACE_LINK_LIBRARIES" "${LIBRARIES_LIBA_DEPENDS_ON}"
    "INTERFACE_INCLUDE_DIRECTORIES" "${LIBA_INCLUDE_DIRECTORIES}"
)

then for libB

add_library(libB ...)
set_target_properties(libB PROPERTIES
    "INTERFACE_LINK_LIBRARIES" "libA"
    "INTERFACE_INCLUDE_DIRECTORIES" "${LIBB_INCLUDE_DIRECTORIES}"
)

The cool thing about this method is that when done properly, you can chain together as many libraries as you like without having to worry about the combinatorial explosion of dependencies and include dirs.

Nicolas Holthaus
  • 7,763
  • 4
  • 42
  • 97
  • I had no idea this existed. This is a nice solution for the include directories. Although it seems like you could leave the `INTERFACE_LINK_LIBRARIES` alone since cmake's `target_link_libraries()` dependencies are transitive in nature by default. According to the docs, setting `INTERFACE_LINK_LIBRARIES` allows you to override the link dependencies derived via `target_link_libraries()`. Interesting. – mshildt Apr 27 '16 at 12:53
  • you could possibly also depend on things which haven't been creating through `add_library` if that made sense, although even in that case I'd probably add an `IMPORTED` target. – Nicolas Holthaus Apr 27 '16 at 13:23
  • Also, I think if you have a case where libC depnds on libB depends on libA, but you, the executable designer, only know about libC (i.e. A and B are implementation details) it's nice to only need to `target_link_libraries(myExe LibC)`. Whenever I've tried to `target_link_libraries` on another lib, it never seemed to work as expected. – Nicolas Holthaus Apr 27 '16 at 13:26
  • `target_link_libraries(myExe LibC)` should definitely work in that case without having to set `INTERFACE_LINK_LIBRARIES`. That is, assuming LibC names a CMake target that is part of your overall project or an imported target. – mshildt Apr 27 '16 at 13:41
2

Taking @epicbrew and @NicolasHolthaus answers/comments I just wanted to show how the complete example would look in a newer version of CMake and discuss some of the implications:

project/CMakeLists.txt

cmake_minimum_required(VERSION 3.0)
project( project )

add_subdirectory( lib/libB )
add_subdirectory( lib/libA )

add_executable( project src/project.cpp )
target_link_libraries( project libB )

project/lib/libA/CMakeLists.txt

cmake_minimum_required(VERSION 3.0)
project( libA )

add_library( libA STATIC src/libA.cpp include/libA.h )
target_include_directories( libA PUBLIC include )

project/lib/libB/CMakeLists.txt

add_library( libB STATIC src/libB.cpp  include/libB.h )
target_include_directories( libB PUBLIC include )
target_link_libraries( libB libA )

As you can see

  1. You need to know in the main CMakeLists.txt file that you also need libA. I was deliberately reversing the add_subdirectory() calls to show that the order doesn't matter when using target_link_libraries() (sort of allows target forward declarations).
  2. You don't need the local include_directories() call anymore it's an implicit part of the target_include_directories().
  3. With target_include_directories(... PUBLIC ...) those include paths are self-propagating.
  4. You don't need to add the ${CMAKE_CURRENT_SOURCE_DIR} prefixes, those are the defaults.
  5. I just added the source/header files directly to the add_executable()/add_library() calls for compactness of this example code. Normally I also would place those in local variables.
  6. libB is not standalone, but you could change that e.g. by adding add_subdirectory( ../libA libA ) to its CMakeLists.txt file. Obviously you then have to remove add_subdirectory( lib/libA ) from the main CMakeLists.txt.
  7. If you want to optimize the CMake processing a little you should not repeat the project() commands. If you still want libA to be self-sufficient you could add if ( CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR )/endif() around the cmake_minimum_required(VERSION 3.0)/project( libA ) calls.
Florian
  • 39,996
  • 9
  • 133
  • 149
1

CMake knows about all of the targets that you've built, so there's no need to export information up to the parent. You simply tell CMake that libB links against libA and CMake takes care of putting the correct path on the linker command line. So just specify libA as a link dependency of libB in your libB/CMakeLists.txt:

target_link_libraries(libB libA)

For the include path you either need to set variables in the parent along the lines of the following in the top level project/CMakeLists.txt:

set(LIBA_INCLUDES libA/include)
set(LIBB_INCLUDES libB/include)

Then reference these in the subprojects CMakeList.txt files.

Or, you could use a relative path in your libB/CMakeLists.txt file:

include_directories(../libA/include)
mshildt
  • 8,782
  • 3
  • 34
  • 41
  • can't you get the include directories by setting a target property on the library, similar to what Qt5 does? – Nicolas Holthaus Apr 26 '16 at 18:33
  • @NicolasHolthaus probably, although I'm not familiar with that particular approach. I'd be interested to see how that works, perhaps you could post an answer? – mshildt Apr 26 '16 at 18:34