2

I have a static library called "Base" with the following CMakeLists.txt:

cmake_minimum_required(VERSION 2.8)
project(Base)
add_library(${PROJECT_NAME} STATIC Base.h Base.cpp )
install(FILES ${PROJECT_NAME}.h DESTINATION include )
install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME}-targets DESTINATION lib)
install(EXPORT ${PROJECT_NAME}-targets DESTINATION lib)
install(FILES ${PROJECT_NAME}-config.cmake DESTINATION lib)

The Base-config.cmake is inspired from CMake tutorials:

get_filename_component(SELF_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
include(${SELF_DIR}/Base-targets.cmake)
get_filename_component(Base_INCLUDE_DIRS "${SELF_DIR}/../include" ABSOLUTE)

Both the compilation and installation of Base work OK.

Now, I've got another distinct static library called "Middle" that depends on "Base". In its CMakeLists.txt, I find the Base package and define the install/export steps like so:

cmake_minimum_required(VERSION 2.8)
project(Middle)
find_package(Base REQUIRED)
if ( Base_FOUND )
    message( "Base found!")
endif()
include_directories(${Base_INCLUDE_DIRS})
add_library(${PROJECT_NAME} STATIC Middle.h Middle.cpp )
target_link_libraries( ${PROJECT_NAME} Base )

install(FILES ${PROJECT_NAME}.h DESTINATION include )
install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME}-targets DESTINATION lib)
install(EXPORT ${PROJECT_NAME}-targets DESTINATION lib)
install(FILES ${PROJECT_NAME}-config.cmake DESTINATION lib)

The Middle-config.cmake file is very similar to Base's one:

get_filename_component(SELF_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
include(${SELF_DIR}/Middle-targets.cmake)
get_filename_component(Middle_INCLUDE_DIRS "${SELF_DIR}/../include" ABSOLUTE)

This works fine: Base is found and Middle compiles and install correctly.

Finally I've got a completely separated "TopApp" executable project that relies on "Middle". Again, in its CMakeLists.txt file, I look for the "Middle" package and use it like this:

cmake_minimum_required(VERSION 2.8)
project(TopApp)
find_package(Middle REQUIRED)
if ( Middle_FOUND )
    message( "Middle found!")
endif()
include_directories(${Middle_INCLUDE_DIRS})
add_executable(TopApp main.cpp )
target_link_libraries( ${PROJECT_NAME} Middle )

As a result, the TopApp compiles OK, and links OK against Middle, but fails to link against Base. However I can see that Base.lib is present in the TopApp project settings (I'm on Windows/Visual Studio). The path to the Middle.lib is absolute and points where it should, whereas for Base, it's just "Base.lib" with no path.

My goal is of course for TopApp just to state that it depends on Middle. Hopefully TopApp.exe should link against Middle.lib and all its dependencies (Base.lib in my case) automatically thanks to CMake import/export system.

I this this should be possible, and I suspect I'm missing something in Middle's CMakeLists.txt or Middle-config.cmake but I'm a bit confused.

(Note that I don't want these 3 projects to be in the same tree (using add_subdirectories basically) because I'm intending to release them separately on some public repositories)

EDIT: After wojciii's reply, I've reworked the XXX-config.cmake files. Here's Base-config.cmake:

# - Config file for the Base package
# It defines the following variables
#  BASE_INCLUDE_DIRS - include directories for Base
#  BASE_LIBRARIES    - libraries to link against

# Compute paths
get_filename_component(BASE_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
get_filename_component(BASE_INCLUDE_DIRS "${BASE_CMAKE_DIR}/../include" ABSOLUTE)

# Our library dependencies (contains definitions for IMPORTED targets)
if(NOT TARGET Base AND NOT Base_BINARY_DIR)
  include("${BASE_CMAKE_DIR}/Base-targets.cmake")
endif()

# These are IMPORTED targets created by BaseTargets.cmake
set(BASE_LIBRARIES Base)

And here's Middle-config.cmake:

# - Config file for the Base package
# It defines the following variables
#  MIDDLE_INCLUDE_DIRS - include directories for Middle
#  MIDDLE_LIBRARIES    - libraries to link against

# Compute paths
get_filename_component(MIDDLE_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
get_filename_component(MIDDLE_INCLUDE_DIRS "${MIDDLE_CMAKE_DIR}/../include" ABSOLUTE)

# Our library dependencies (contains definitions for IMPORTED targets)
if(NOT TARGET Middle AND NOT Middle_BINARY_DIR)
  include("${MIDDLE_CMAKE_DIR}/Middle-targets.cmake")
  #include("${MIDDLE_CMAKE_DIR}/Base-targets.cmake")
endif()

# These are IMPORTED targets created by MiddleTargets.cmake
set(MIDDLE_LIBRARIES Middle Base)

This is cleaner. I've also added Base to the MIDDLE_LIBRARIES variable (last line) as suggested. However this doesn't solve the problem: Base is an unknown target at the TopApp level and link fails. I think I should export Base-config/targets files at the Middle level, but when I add that to Middle's CMakeLists.txt it complains:

install TARGETS given target "Base" which does not exist in this directory.

It seems Base is an unknown target for install, but I can still do that in Middle's CMakeLists:

get_property(Base_location TARGET Base PROPERTY LOCATION)
message("Base_location == ${Base_location}")
user3742582
  • 113
  • 2
  • 5
  • Is adding `Base` library name in the `target_link_libraries` directive (like `target_link_libraries( ${PROJECT_NAME} Middle Base)`) solution for you? – Gluttton Jun 15 '14 at 16:37
  • Unfortunately no. I'd like TopApp to simply "target_link_libraries" to Middle and behind the scene have Base.lib come with it. Also I've tried adding "Base" to TopApp's target_link_libraries as you mentionned just to check, but it doesn't work as the Base target is unknown there. – user3742582 Jun 15 '14 at 18:05
  • [This question](http://stackoverflow.com/questions/24232941/linking-against-a-static-lib-using-many-other-static-libs) looks like related with your problem. – Gluttton Jun 15 '14 at 18:56
  • This is related indeed. However I expect CMake dependency system to help me with that. It does so very well when all targets are in the same tree, but I can't get it to do that with exported/imported targets. – user3742582 Jun 16 '14 at 08:56
  • Ah. I think that know what is wrong. The linker does not know about CMake targets - it knows libraries. So it needs "-lBase -L/path/to/lib" or any other way of specifying what to link instead of CMake targets. – wojciii Jun 16 '14 at 10:16

1 Answers1

1

I get the impression from reading http://www.itk.org/Wiki/CMake/Tutorials/How_to_create_a_ProjectConfig.cmake_file (or see http://www.cmake.org/cmake/help/v3.0/manual/cmake-packages.7.html for v. 3.x) that

Middle-config.cmake should set the following variables:

MIDDLE_INCLUDE_DIRS - include directories for Middle MIDDLE_LIBRARIES - libraries to link against MIDDLE_EXECUTABLE - the bar executable

so that users of this config file which call find_package() use them to make their linker happy.

You are already calling include_directories() with values set in Middle-config.cmake so I believe that you should call target_link_libraries() using MIDDLE_LIBRARIES.

wojciii
  • 4,253
  • 1
  • 30
  • 39
  • A more up-to-date (and less convoluted) version of the linked page is [available as part of the CMake 3.0 manual](http://www.cmake.org/cmake/help/v3.0/manual/cmake-packages.7.html). – ComicSansMS Jun 16 '14 at 09:22
  • Thank you. I assumed that user wanted cmake 2.8. I added a link to the cmake 3.x document. – wojciii Jun 16 '14 at 09:31
  • Fortunately, most of the information on that page also applies to earlier versions of CMake. I just remember first reading about config files on the same wiki page that you linked and I had a hard time figuring out how it worked. Hopefully, the new page does a better job at explaining the system. – ComicSansMS Jun 16 '14 at 10:10