user3742582
user3742582

Reputation: 123

CMake: importing a static lib target and all the libs it depends on

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}")

Upvotes: 2

Views: 3372

Answers (1)

wojciii
wojciii

Reputation: 4325

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.

Upvotes: 1

Related Questions